﻿using System;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;

namespace SharpCommon.Extension
{
    internal class DataTableEntityBuilder<T> where T : class,new()
    {
        //private static readonly MethodInfo GetValueMethod = typeof(T).GetMethod("get_Item", new Type[] { typeof(int) });

        //private static readonly MethodInfo IsDBNullMethod = typeof(T).GetMethod("IsNull", new Type[] { typeof(int) });

        //private Func<DataRow, T> _handler;

        private delegate T Load(DataRow dataRow);

        private Load _handler;

        private DataTableEntityBuilder()
        {
            //GetValueMethod = typeof(T).GetMethod("get_Item", new Type[] { typeof(int) });

            //IsDBNullMethod = typeof(T).GetMethod("IsNull", new Type[] { typeof(int) });
        }

        public T Build(DataRow dataRow)
        {
            return this._handler(dataRow);
            //return this
        }

        public static DataTableEntityBuilder<T> CreateBuilder(DataRow dataRow)
        {
            DataTableEntityBuilder<T> builder = new DataTableEntityBuilder<T>();

            Type entityType = typeof(T);

            DynamicMethod dynamicMethod = new DynamicMethod("DynamicCreatyEntity",
                                                                                       typeof(T),
                                                                                       new Type[] { typeof(DataRow) },
                                                                                       typeof(T),
                                                                                       true);
            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
            LocalBuilder localBuilder = ilGenerator.DeclareLocal(entityType);
            ilGenerator.Emit(OpCodes.Newobj, entityType.GetConstructor(Type.EmptyTypes));
            ilGenerator.Emit(OpCodes.Stloc, localBuilder);

            for (int i = 0; i < dataRow.ItemArray.Length; i++)
            {
                PropertyInfo propertyInfo = entityType.GetProperty(dataRow.Table.Columns[i].ColumnName);
                Label label = ilGenerator.DefineLabel();

                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldc_I4, i);
                ilGenerator.Emit(OpCodes.Callvirt, ReflectionMethod.IsDBNullMethod);//DataTableEntityBuilder<T>.IsDBNullMethod);// IsDBNullMethod);
                ilGenerator.Emit(OpCodes.Brtrue, label);
                ilGenerator.Emit(OpCodes.Ldloc, localBuilder);
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldc_I4, i);
                ilGenerator.Emit(OpCodes.Callvirt, ReflectionMethod.GetValueMethod);//DataTableEntityBuilder<T>.GetValueMethod); //GetValueMethod);
                ilGenerator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                ilGenerator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());

                ilGenerator.MarkLabel(label);
            }

            ilGenerator.Emit(OpCodes.Ldloc, localBuilder);
            ilGenerator.Emit(OpCodes.Ret);

            //builder._handler = (Func<DataRow, T>)dynamicMethod.CreateDelegate(typeof(Func<>));
            builder._handler = (Load)dynamicMethod.CreateDelegate(typeof(Load));

            return builder;
        }
    }

    internal class ReflectionMethod
    {
        public static readonly MethodInfo GetValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });

        public static readonly MethodInfo IsDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
    }
}
